package com.example.sbbsec.domain.stat.household.impl;

import com.example.sbbsec.domain.constantenum.ProvinceEnum;
import com.example.sbbsec.domain.echarts.BarSeriesEntity;
import com.example.sbbsec.domain.echarts.MapSeriesEntity;
import com.example.sbbsec.domain.echarts.PieSeriesEntity;
import com.example.sbbsec.domain.stat.common.ExamRegistration;
import com.example.sbbsec.domain.stat.common.PlanSummaryEntity;
import com.example.sbbsec.domain.stat.household.HouseHoldService;
import com.example.sbbsec.domain.stat.household.entity.HouseHoldDistributionEntity;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.example.sbbsec.domain.ConfigConstant.*;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.Lists.newArrayList;
import static java.lang.Boolean.TRUE;
import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap;

/**
 * 户籍分布统计接口实现类
 *
 * @author lowe.yang
 * @date 2018-12-03 10:53
 */
@Service
public class HouseHoldServiceImpl implements HouseHoldService {


    @Override
    public HouseHoldDistributionEntity getHouseHoldDistribution(String code) {
        //在redis中查询 所有考生信息
        final List<ExamRegistration> examRegistrations = newArrayList();
        final HouseHoldDistributionEntity houseHoldDistribution = new HouseHoldDistributionEntity();

        //计划信息
        getPlanSummary(examRegistrations, houseHoldDistribution);
        //学校top10
        getSchoolTop10(examRegistrations, houseHoldDistribution);
        //岗位top10
        getJobTop10(examRegistrations, houseHoldDistribution);
        //地图
        getHouseHoldMap(examRegistrations, houseHoldDistribution, code);
        //学历饼图
        getEducationDistribution(examRegistrations, houseHoldDistribution, code);
        //性别饼图
        getGenderDistribution(examRegistrations, houseHoldDistribution, code);
        //年龄饼图
        getAgeDistribution(examRegistrations, houseHoldDistribution, code);
        //学校饼图
        getSchoolDistribution(examRegistrations, houseHoldDistribution, code);

        return houseHoldDistribution;
    }

    /**
     * 获取报考计划人数统计信息
     * 包括:
     * 当前在线人数
     * 当前报名人数
     * 相似专业人数
     * 审核未通过人数
     * 审核通过人数
     */
    private void getPlanSummary(List<ExamRegistration> examRegistrations, HouseHoldDistributionEntity houseHoldDistribution) {
        final PlanSummaryEntity planSummary = new PlanSummaryEntity();
        // TODO: 12/4/2018  在线人数 session总数
        planSummary.setOnLineCount(666);
        //当前报名人数
        planSummary.setSignUpCount(examRegistrations.size());

        //相似专业人数
        final Integer similarCount = examRegistrations.stream().filter(item -> Longs.compare(item.getSimilarSpecializedMark(), 1L) == 0)
                .collect(Collectors.toList())
                .size();
        planSummary.setSimilarCount(similarCount);

        //审核通过人数
        final Integer auditPassCount = examRegistrations.stream().filter(item -> Ints.compare(item.getStatusId(), 119) == 0)
                .collect(Collectors.toList())
                .size();
        planSummary.setAuditPassCount(auditPassCount);

        //审核未通过
        planSummary.setAuditNotPassCount(planSummary.getSignUpCount() - auditPassCount);


        houseHoldDistribution.setPlanSummary(planSummary);
        houseHoldDistribution.setTotalCount(planSummary.getSignUpCount());
    }

    /**
     * 获取当前报考计划中热门学校 top10
     */
    private void getSchoolTop10(List<ExamRegistration> examRegistrations, HouseHoldDistributionEntity houseHoldDistribution) {
        //获取学校排名top10
        final List<Pair<String, Integer>> top10 = getRanking(examRegistrations, ExamRegistration::getGraduateSchool);
        //构建柱形图
        final List<BarSeriesEntity> barSeries = newArrayList();
        for (Pair<String, Integer> pair : top10) {
            barSeries.add(new BarSeriesEntity(pair.getKey(), pair.getValue()));
        }

        houseHoldDistribution.setSchoolTop10(barSeries);
    }

    /**
     * 获取当前报考计划中热门岗位 top10
     */
    private void getJobTop10(List<ExamRegistration> examRegistrations, HouseHoldDistributionEntity houseHoldDistribution) {
        //获取学校岗位top10
        final List<Pair<String, Integer>> top10 = getRanking(examRegistrations, ExamRegistration::getJobName);
        //构建柱形图
        final List<BarSeriesEntity> barSeries = newArrayList();
        for (Pair<String, Integer> pair : top10) {
            barSeries.add(new BarSeriesEntity(pair.getKey(), pair.getValue()));
        }

        houseHoldDistribution.setJobTop10(barSeries);
    }

    /**
     * 获取当前户籍分布数据
     */
    private void getHouseHoldMap(List<ExamRegistration> examRegistrations
            , HouseHoldDistributionEntity houseHoldDistribution, String code) {
        //根据省份分组求数量
        final Map<String, Integer> provinceMap = countByGroup(examRegistrations, ExamRegistration::getProvinceCode);


        final List<MapSeriesEntity> mapSeries = newArrayList();
        provinceMap.forEach((key, value) -> {
            final ProvinceEnum province = ProvinceEnum.getByCode(key);
            final MapSeriesEntity mapData = new MapSeriesEntity(province.getName(), province.getCode(), String.valueOf(value));

            //传入的省份编码 为选中 并且人数变成选中的省份的人数
            if (Objects.equals(province.getCode(), code)) {
                mapData.setSelected(TRUE);
                houseHoldDistribution.setTotalCount(value);
            }
            mapSeries.add(mapData);
        });

        houseHoldDistribution.setHouseHoldDistribution(mapSeries);
    }

    /**
     * 根据省份编码获取学历分布饼图数据
     */
    private void getEducationDistribution(List<ExamRegistration> examRegistrations
            , HouseHoldDistributionEntity houseHoldDistribution, String code) {
        //构建饼图
        final List<PieSeriesEntity> pieSeries =
                getPieData(examRegistrations, ExamRegistration::getEducationName, code);

        houseHoldDistribution.setEducationDistribution(pieSeries);
    }

    /**
     * 根据省份编码获取性别分布饼图数据
     */
    private void getGenderDistribution(List<ExamRegistration> examRegistrations
            , HouseHoldDistributionEntity houseHoldDistribution, String code) {
        //构建饼图
        final List<PieSeriesEntity> pieSeries =
                getPieData(examRegistrations, ExamRegistration::getGenderName, code);

        houseHoldDistribution.setGenderDistribution(pieSeries);
    }

    /**
     * 根据省份编码获取年龄分布饼图数据
     */
    private void getAgeDistribution(List<ExamRegistration> examRegistrations
            , HouseHoldDistributionEntity houseHoldDistribution, String code) {
        //构建饼图
        final List<PieSeriesEntity> pieSeries =
                getPieData(examRegistrations, ExamRegistration::getAgeRange, code);

        houseHoldDistribution.setAgeDistribution(pieSeries);
    }

    /**
     * 根据省份编码获取学校分布饼图数据
     */
    private void getSchoolDistribution(List<ExamRegistration> examRegistrations
            , HouseHoldDistributionEntity houseHoldDistribution, String code) {
        //构建饼图
        final List<PieSeriesEntity> pieSeries =
                getPieData(examRegistrations, ExamRegistration::getGraduateSchool, code);

        houseHoldDistribution.setSchoolDistribution(pieSeries);
    }

    /**
     * 获取饼图数据
     * 1,如果有省份编码 则想过滤数据
     * 2,根据条件分组
     * 3,构建饼图实体
     *
     * @param examRegistrations 原数据集合
     * @param groupBy           分组条件
     * @param code              省份编码
     * @return 饼图数据
     */
    private List<PieSeriesEntity> getPieData(List<ExamRegistration> examRegistrations, Function<ExamRegistration, String> groupBy
            , String code) {
        //如果存在省份编码 则需要过滤数据
        final List<ExamRegistration> origins = filterByProvince(examRegistrations, code);
        //根据条件段分组
        final Map<String, Integer> map = groupWithMax(origins, groupBy);

        //构建饼图
        final List<PieSeriesEntity> pieSeries = newArrayList();
        map.forEach((key, value) -> pieSeries.add(new PieSeriesEntity(key, value)));

        return pieSeries;
    }


    /**
     * 根据省份编码过滤数据
     */
    private List<ExamRegistration> filterByProvince(List<ExamRegistration> examRegistrations, String code) {
        //如果存在省份编码 则需要过滤数据
        List<ExamRegistration> res;
        if (isNullOrEmpty(code)) {
            res = examRegistrations;
        } else {
            res = examRegistrations.stream()
                    .filter(item -> Objects.equals(item.getProvinceCode(), code))
                    .collect(Collectors.toList());
        }

        return res;
    }


    /**
     * 根据分组条件 返回分组排名前n结果
     *
     * @param groupBy 分组条件
     * @return 前n
     */
    private <T, E> List<Pair<T, Integer>> getRanking(List<E> list, Function<E, T> groupBy) {
        //分组统计数量
        final Map<T, Integer> countMap = countByGroup(list, groupBy);
        //对value排序 选出最大的前n个
        final List<Map.Entry<T, Integer>> entityList = newArrayList(countMap.entrySet());
        //排序
        final List<Map.Entry<T, Integer>> sortedList = entityList.stream()
                .sorted((p1, p2) -> Ints.compare(p2.getValue(), p1.getValue()))
                .collect(Collectors.toList());
        //取前n结果
        final List<Pair<T, Integer>> res = newArrayList();

        sortedList.subList(0, Math.min(RANKING_DATA, sortedList.size()))
                .forEach(item -> res.add(new MutablePair<>(item.getKey(), item.getValue())));

        return res;
    }

    /**
     * 根据条件分组查询结果
     * 返回结果有最大数量限制
     * 超出限制的部分 统一在  "其他" 中显示
     *
     * @param groupBy 分组条件
     * @return 分组统计结果
     */
    private Map<String, Integer> groupWithMax(List<ExamRegistration> examRegistrations
            , Function<ExamRegistration, String> groupBy) {
        //分组统计数量
        final Map<String, Integer> countMap = countByGroup(examRegistrations, groupBy);
        if (countMap.size() <= PIE_DATA) {
            return countMap;
        }
        //对value排序 选出最大的前n个
        final List<Map.Entry<String, Integer>> entityList = newArrayList(countMap.entrySet());
        //排序
        final List<Map.Entry<String, Integer>> sortedList = entityList.stream()
                .sorted((p1, p2) -> Ints.compare(p2.getValue(), p1.getValue()))
                .collect(Collectors.toList());
        //取前n结果
        final Map<String, Integer> res = newHashMap();
        sortedList.subList(0, PIE_DATA)
                .forEach(item -> res.put(item.getKey(), item.getValue()));
        //剩下的结果放在其他里
        int otherCount = sortedList.subList(PIE_DATA, sortedList.size())
                .stream().mapToInt(Map.Entry::getValue).sum();
        res.put(OTHER, otherCount);

        return res;
    }


    /**
     * 根据条件分组查询结果
     *
     * @param groupBy 分组条件
     * @return 分组统计结果
     */
    private <T, E> Map<T, Integer> countByGroup(List<E> list, Function<E, T> groupBy) {
        //根据条件分组
        final Map<T, List<E>> groupMap = list.stream().collect(Collectors.groupingBy(groupBy));
        //分别统计数量
        final Map<T, Integer> countMap = newHashMap();
        groupMap.forEach((key, value) -> countMap.put(key, value.size()));

        return countMap;
    }

}
